/*
* 语音听写(iFly Auto Transform)技术能够实时地将语音转换成对应的文字。
*/
 
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "qisr.h"
#include "msp_cmn.h"
#include "msp_errors.h"
#include "speech_recognizer.h"
#include <iconv.h>

#include "qtts.h"
#include "msp_cmn.h"
#include "msp_errors.h"
 
#include "ros/ros.h"
#include "std_msgs/String.h"
#include "myxfei/voiceMsg.h"
#include "myxfei/ttsMsg.h"
 
#define FRAME_LEN   640 
#define BUFFER_SIZE 4096
int state = 0;
typedef struct tmpdata
{
    char* name;
    char* thing;
    bool Finish;
}tmpdata;
tmpdata data = {
    NULL,
    NULL,
    false
};

/*****************************语音合成部分***********************************/
const char* Filename = "voice.wav";
const char* PlayPath = "play voice.wav";
/* wav音频头部格式 */
typedef struct _wave_pcm_hdr
{
	char            riff[4];                // = "RIFF"
	int				size_8;                 // = FileSize - 8
	char            wave[4];                // = "WAVE"
	char            fmt[4];                 // = "fmt "
	int				fmt_size;				// = 下一个结构体的大小 : 16

	short int       format_tag;             // = PCM : 1
	short int       channels;               // = 通道数 : 1
	int				samples_per_sec;        // = 采样率 : 8000 | 6000 | 11025 | 16000
	int				avg_bytes_per_sec;      // = 每秒字节数 : samples_per_sec * bits_per_sample / 8
	short int       block_align;            // = 每采样点字节数 : wBitsPerSample / 8
	short int       bits_per_sample;        // = 量化比特数: 8 | 16

	char            data[4];                // = "data";
	int				data_size;              // = 纯数据长度 : FileSize - 44 
} wave_pcm_hdr;

/* 默认wav音频头部数据 */
wave_pcm_hdr default_wav_hdr = 
{
	{ 'R', 'I', 'F', 'F' },
	0,
	{'W', 'A', 'V', 'E'},
	{'f', 'm', 't', ' '},
	16,
	1,
	1,
	16000,
	32000,
	2,
	16,
	{'d', 'a', 't', 'a'},
	0  
};
int text_to_speech(const char* src_text, const char* des_path, const char* params)
{
	int          ret          = -1;
	FILE*        fp           = NULL;
	const char*  sessionID    = NULL;
	unsigned int audio_len    = 0;
	wave_pcm_hdr wav_hdr      = default_wav_hdr;
	int          synth_status = MSP_TTS_FLAG_STILL_HAVE_DATA;

	if (NULL == src_text || NULL == des_path)
	{
		printf("params is error!\n");
		return ret;
	}
	fp = fopen(des_path, "wb");
	if (NULL == fp)
	{
		printf("open %s error.\n", des_path);
		return ret;
	}
	/* 开始合成 */
	sessionID = QTTSSessionBegin(params, &ret);
	if (MSP_SUCCESS != ret)
	{
		printf("QTTSSessionBegin failed, error code: %d.\n", ret);
		fclose(fp);
		return ret;
	}
	ret = QTTSTextPut(sessionID, src_text, (unsigned int)strlen(src_text), NULL);
	if (MSP_SUCCESS != ret)
	{
		printf("QTTSTextPut failed, error code: %d.\n",ret);
		QTTSSessionEnd(sessionID, "TextPutError");
		fclose(fp);
		return ret;
	}
	printf("正在合成 ...\n");
	fwrite(&wav_hdr, sizeof(wav_hdr) ,1, fp); //添加wav音频头，使用采样率为16000
	while (1) 
	{
		/* 获取合成音频 */
		const void* data = QTTSAudioGet(sessionID, &audio_len, &synth_status, &ret);
		if (MSP_SUCCESS != ret)
			break;
		if (NULL != data)
		{
			fwrite(data, audio_len, 1, fp);
		    wav_hdr.data_size += audio_len; //计算data_size大小
		}
		if (MSP_TTS_FLAG_DATA_END == synth_status)
			break;
	}
	printf("\n");
	if (MSP_SUCCESS != ret)
	{
		printf("QTTSAudioGet failed, error code: %d.\n",ret);
		QTTSSessionEnd(sessionID, "AudioGetError");
		fclose(fp);
		return ret;
	}
	/* 修正wav文件头数据的大小 */
	wav_hdr.size_8 += wav_hdr.data_size + (sizeof(wav_hdr) - 8);
	
	/* 将修正过的数据写回文件头部,音频文件为wav格式 */
	fseek(fp, 4, 0);
	fwrite(&wav_hdr.size_8,sizeof(wav_hdr.size_8), 1, fp); //写入size_8的值
	fseek(fp, 40, 0); //将文件指针偏移到存储data_size值的位置
	fwrite(&wav_hdr.data_size,sizeof(wav_hdr.data_size), 1, fp); //写入data_size的值
	fclose(fp);
	fp = NULL;
	/* 合成完毕 */
	ret = QTTSSessionEnd(sessionID, "Normal");
	if (MSP_SUCCESS != ret)
	{
		printf("QTTSSessionEnd failed, error code: %d.\n",ret);
	}

	return ret;
}

void tts(const char* text,const char* filename)
{
	int         ret                  = MSP_SUCCESS;
	const char* login_params         = "appid = 5e81ee6d, work_dir = /home/zhachery/catkin_ws/devel/lib/voicecom/";//dir目录的目的是为了找到msc文件夹下的jet文件
	/*
	* rdn:           合成音频数字发音方式
	* volume:        合成音频的音量
	* pitch:         合成音频的音调
	* speed:         合成音频对应的语速
	* voice_name:    合成发音人
	* sample_rate:   合成音频采样率
	* text_encoding: 合成文本编码格式
	*
	*/
	const char* session_begin_params = "engine_type = local,voice_name=xiaoyan, text_encoding = UTF8, tts_res_path = fo|res/tts/xiaoyan.jet;fo|res/tts/common.jet, sample_rate = 16000, speed = 50, volume = 50, pitch = 50, rdn = 2";
	//const char* filename             = "tts_sample.wav"; //合成的语音文件名称
	//const char* text                 = "Hello,what's your name?"; //合成文本
	/* 用户登录 */
	ret = MSPLogin(NULL, NULL, login_params); //第一个参数是用户名，第二个参数是密码，第三个参数是登录参数，用户名和密码可在http://www.xfyun.cn注册获取
	if (MSP_SUCCESS != ret)
	{
		printf("MSPLogin failed, error code: %d.\n", ret);
		goto exit ;//登录失败，退出登录
	}

	/* 文本合成 */
	//printf("开始合成 ...\n");
	ret = text_to_speech(text, filename, session_begin_params);
	if (MSP_SUCCESS != ret)
	{
		printf("text_to_speech failed, error code: %d.\n", ret);
	}
	//printf("合成完毕\n");

exit:
	MSPLogout(); //退出登录
}

void PlayWav()
{
	system(PlayPath);
}

/**************************************************************/


/***********************************语音识别部分********************************************/
static void show_result(char *string, char is_over)
{  
    printf("\rI hear: %s ", string);
    if(is_over)
        putchar('\n');
}
 
static char *g_result = NULL;
static unsigned int g_buffersize = BUFFER_SIZE;
 
void on_result(const char *result, char is_last)
{
    if (result) {
        size_t left = g_buffersize - 1 - strlen(g_result);
        size_t size = strlen(result);
        if (left < size) {
            g_result = (char*)realloc(g_result, g_buffersize + BUFFER_SIZE);
            if (g_result)
                g_buffersize += BUFFER_SIZE;
            else {
                printf("mem alloc failed\n");
                return;
            }
        }
        strncat(g_result, result, size);
        show_result(g_result, is_last);
    }
}
 
void on_speech_begin()
{
    if (g_result)
    {
        free(g_result);
    }
    g_result = (char*)malloc(BUFFER_SIZE);
    g_buffersize = BUFFER_SIZE;
    memset(g_result, 0, g_buffersize);
 
    printf("Start Listening...\n");
}
void on_speech_end(int reason)
{
    if (reason == END_REASON_VAD_DETECT)
        printf("\nSpeaking done \n");
    else
        printf("\nRecognizer error %d\n", reason);
}
 
/* demo recognize the audio from microphone */
static void demo_mic(const char* session_begin_params)
{
    int errcode;
    int i = 0;
 
    struct speech_rec iat;
 
    struct speech_rec_notifier recnotifier = {
        on_result,
        on_speech_begin,
        on_speech_end
    };
 
    errcode = sr_init(&iat, session_begin_params, SR_MIC, &recnotifier);
    if (errcode) {
        printf("speech recognizer init failed\n");
        return;
    }
    errcode = sr_start_listening(&iat);
    if (errcode) {
        printf("start listen failed %d\n", errcode);
    }
    /* demo 10 seconds recording */
    while(i++ < 10)
        sleep(1);
    errcode = sr_stop_listening(&iat);
    if (errcode) {
        printf("stop listening failed %d\n", errcode);
    }
    sr_uninit(&iat);
}

char* GetName(char* str)
{
    //My name is ***.
	const char* word1 = "is ";
	int i,j;
	for(i = 0;i<strlen(str);i++)
	{
		for(j = 0;word1[j]&&(i+j)<strlen(str)&&word1[j] == str[i+j];j++);
		if(!word1[j])
		{
			i = i+j;
			for(j=0;str[j]&&(i+j)<strlen(str)&&str[i+j]!='.'&&str[i+j]!=' ';j++);
			char* name = (char*)malloc(j);
			strncpy(name,str+i,j);
            printf("%s\n",name);
			return name;
		}
	}
	return NULL;
}
char* GetThing(char* str)
{
    //Please bring me a **.
    const char* keyword = "me ";
    int i,j;
    for(i = 0;i < strlen(str);i++)
    {
        for(j = 0;keyword[j] && (i+j) < strlen(str) && keyword[j] == str[i+j];j++);
        if(!keyword[j])
        {
            i=i+j;
            for(j = 0;(i+j) < strlen(str) && str[i+j] != ' ';j++);
            i = i+j+1;
            for(j = 0;(i+j) < strlen(str) && str[i+j]!='.'&&str[i+j]!=' ';j++);
            char* thing = (char*)malloc(j);
            strncpy(thing,str+i,j);
            printf("%s\n",thing);
            return thing;
        }
    }
    return NULL;
}
char* VoiceRecognition(bool type)
{
    /*
    type = 0 提取姓名
    type= 1 提取物品
    */

    int ret = MSP_SUCCESS;
    const char* login_params = "appid = 5e81ee6d, work_dir = .";

    const char* session_begin_params =
        "sub = iat, domain = iat, language = zh_cn, "
        "accent = mandarin, sample_rate = 16000, "
        "result_type = plain, result_encoding = utf8";

    ret = MSPLogin(NULL, NULL, login_params);
    if(MSP_SUCCESS != ret){
        MSPLogout();
        printf("MSPLogin failed , Error code %d.\n",ret);
    }

    printf("Speak in 10 seconds\n");
    demo_mic(session_begin_params);
    if(!type)
    {
        printf("get name : ");
        MSPLogout();
        return GetName(g_result);
    }
    else
    {
        printf("get thing : ");
        MSPLogout();
        return GetThing(g_result);
    }
    MSPLogout();
}

/***************回调函数**********************/
 
void voiceWakeUp(const myxfei::voiceMsg::ConstPtr& msg)
{
    //state++;
    //printf("voicewakeUp\n");
    char* result = VoiceRecognition(msg->type);
    if(msg->type)
    {
        data.thing = result;
        data.Finish = true;
    }
    else
    {
        data.name = result;
    }
}
void ttsCallback(const myxfei::ttsMsg::ConstPtr& msg)
{
    //state++;
    
	std::cout<<"(I am saying) \n"<<msg->voice.c_str();
	tts(msg->voice.c_str(),Filename);
	PlayWav();
}
void writeData()
{
    FILE* file;//
    file = fopen("/home/zhachery/catkin_ws/src/myxfei/data.txt","a");
    if(file == NULL)
    {
        printf("txt打开错误\n");
    }

    fprintf(file,"%s - %s \n",data.name,data.thing);

    fclose(file);
}
 
int main(int argc, char* argv[])
{
    // 初始化ROS
    ros::init(argc, argv, "voiceRecognition");
    ros::NodeHandle n;
    ros::Rate loop_rate(10);
 
    // 声明Publisher和Subscriber
    // 订阅
    ros::Subscriber VoiceWakeUpSub = n.subscribe("voicewakeup", 1000, voiceWakeUp);  
    ros::Subscriber ttsSub = n.subscribe("ttswakeup",1000,ttsCallback); 
    // 发布  
    ros::Publisher VoiceWakeUpPub = n.advertise<myxfei::voiceMsg>("voicewakeup", 1000);  
    ros::Publisher ttsPub = n.advertise<myxfei::ttsMsg>("ttswakeup", 1000); 
 
    ROS_INFO("Start!");

    const char* msg1 = "Hello,what's your name?";
    const char* msg2 = "What can I help you?";
    myxfei::ttsMsg tts_msg;
    myxfei::voiceMsg voice_msg;

    FILE* file;
    tts_msg.voice = msg1;
    ttsPub.publish(tts_msg);


    while(ros::ok())
    {
        //ROS_INFO("Sleeping...");
        /*switch(state)
        {
            case 0:
                //询问1
                tts_msg.voice = msg1;
                ttsPub.publish(tts_msg);
                break;
            case 1:
                //回答1
                voice_msg.type = 0;//提取名称type=0
                VoiceWakeUpPub.publish(voice_msg);
                break;
            case 2:
                //question2
                tts_msg.voice = msg2;
                ttsPub.publish(tts_msg);
                break;
            case 3:
                //answer2
                voice_msg.type = 1;//提取名称type=0
                VoiceWakeUpPub.publish(voice_msg);
                break;
            default:
                std::cout<<"end\n";
                break;
        }*/
        if(data.Finish == 1)
        {
            writeData();
            //printf("%s--%s \n",data.name,data.thing);
            char* response = (char*)malloc(100);
            sprintf(response,"Your name is %s,and you want %s.",data.name,data.thing);
            printf("%s",response);
            tts_msg.voice = response;
            ttsPub.publish(tts_msg);
            free(response);
            data.Finish = false;
        }

        ros::spinOnce();
        loop_rate.sleep();
    }


    ros::spin();

 
    return 0;
}
